#pragma once
#include <iosfwd>
#include <vector>
#include <iterator>
#include "PV2D.h"

using std::vector;
using std::ostream;
using std::ostream_iterator;

namespace geometry {
class Polygon2D
{
public:
	typedef vector<Point2D>::const_iterator ConstIterator;
	typedef vector<Point2D>::iterator Iterator;

public:
	Polygon2D(void){}
	Polygon2D(const Point2D* first, const Point2D* last) : vertexes(first, last) {}
	~Polygon2D(void){}

public:
	void Add(const Point2D& pt) { vertexes.push_back(pt); }

	void Insert(const int idx, const Point2D& pt)
  { 
    vertexes.insert(vertexes.begin()+idx, pt);
  }

	void Remove(const int idx) { vertexes.erase(vertexes.begin()+idx); }
	void Clear() { vertexes.clear(); }
	ConstIterator Begin() const { return vertexes.begin(); }
	Iterator Begin() { return vertexes.begin(); }
	Iterator End() { return vertexes.end(); }
	ConstIterator End() const { return vertexes.end(); }

	void Assign(ConstIterator first, ConstIterator last) 
  { 
    vertexes.assign(first, last);
  }

public:
	// Test the orientation of a simple polygon
	// Return:	>0 for counterclockwise
	//			=0 for none(degenerate)
	//			<0 for clockwise
	// Note: this algorithm is faster than computing the signed area
	double Orientation(void) const;

	// Computes the area of a 2D polygon
	// Return: the area of the polygon
	double Area(void) const;

	// Crossing number test for a point in a polygon
	bool PtIn_CN(const Point2D& pt) const { return PtIn_CN_JW(pt); }

	// Winding number test for a point in a polygon
	bool PtIn_WN(const Point2D& pt) const;

public:
	int Size() const { return static_cast<int>(vertexes.size()); }
	Point2D& operator[](const int pos) { return vertexes[pos]; }
	const Point2D& operator[](const int pos) const { return vertexes[pos]; }

private:
	bool PtIn_CN_WRF(const Point2D& pt) const;
	bool PtIn_CN_JOR(const Point2D& pt) const;
	bool PtIn_CN_JW(const Point2D& pt) const;

private:
	mutable vector<Point2D> vertexes;
	friend ostream& operator<<(ostream& os, const Polygon2D& pg)
  {
		copy(pg.vertexes.begin(), pg.vertexes.end(),
      ostream_iterator<Point2D>(os, "\n"));
		return os;
	}
};
}
